package com.tl.satoken.netty;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.tl.satoken.domain.entity.GameMessage;
import com.tl.satoken.domain.entity.Player;
import com.tl.satoken.domain.entity.Position;
import com.tl.satoken.domain.entity.MBTISquare;
import com.tl.satoken.enums.MessageType;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.codec.http.websocketx.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.concurrent.ConcurrentHashMap;

@Slf4j
@Component
public class WebSocketFrameHandler extends SimpleChannelInboundHandler<Object> {

    private static final Map<String, ChannelHandlerContext> clients = new ConcurrentHashMap<>();
    private static final Map<String, String> playerRoomMap = new ConcurrentHashMap<>();
    private static final Map<String, Player> roomPlayers = new ConcurrentHashMap<>();
    private static final Map<String, MBTISquare> roomMbtiMap = new ConcurrentHashMap<>();
    
    private static ChannelGroupManager channelGroupManager;
    private static ObjectMapper objectMapper = new ObjectMapper();
    
    @Autowired
    public void setChannelGroupManager(ChannelGroupManager channelGroupManager) {
        WebSocketFrameHandler.channelGroupManager = channelGroupManager;
    }

    @Override
    protected void channelRead0(ChannelHandlerContext ctx, Object msg) {
        if (msg instanceof TextWebSocketFrame) {
            String text = ((TextWebSocketFrame) msg).text();
            String clientId = ctx.channel().id().asShortText();
            log.info("收到客户端[{}]消息: {}", clientId, text);
            
            try {
                GameMessage gameMessage = objectMapper.readValue(text, GameMessage.class);
                handleGameMessage(ctx, clientId, gameMessage);
            } catch (Exception e) {
                log.error("解析游戏消息失败", e);
            }
        } else if (msg instanceof CloseWebSocketFrame) {
            log.info("客户端请求关闭连接");
            ctx.channel().close();
        } else if (msg instanceof PingWebSocketFrame) {
            ctx.channel().writeAndFlush(new PongWebSocketFrame());
        }
    }
    
    private void handleGameMessage(ChannelHandlerContext ctx, String clientId, GameMessage message) {
        try {
            switch (message.getType()) {
                case JOIN_GAME:
                    handleJoinGame(ctx, clientId, message);
                    break;
                case LEAVE_GAME:
                    handleLeaveGame(ctx, clientId, message);
                    break;
                case UPDATE_PLAYER:
                    handleUpdatePlayer(ctx, clientId, message);
                    break;
                case EMOJI_SENT:
                    handleEmojiSent(ctx, clientId, message);
                    break;
                default:
                    log.warn("未知消息类型: {}", message.getType());
            }
        } catch (Exception e) {
            log.error("处理游戏消息失败", e);
            sendError(ctx, "处理消息失败: " + e.getMessage());
        }
    }
    
    private void handleJoinGame(ChannelHandlerContext ctx, String clientId, GameMessage message) {
        try {
            Player player = objectMapper.convertValue(message.getData(), Player.class);
            player.setSessionId(clientId);
            
            String roomId = findOrCreateRoom(player.getMbtiType());
            if (roomId != null) {
                Position startPos = generateRandomPosition(roomId);
                player.setPosition(startPos);
                player.setRoomId(roomId);
                player.setStatus("online");
                
                channelGroupManager.addPlayerToRoom(roomId, player, ctx.channel());
                roomPlayers.put(player.getId(), player);
                playerRoomMap.put(player.getId(), roomId);
                
                broadcastRoomUpdate(roomId);
                log.info("玩家 {} 成功加入房间 {}", player.getUsername(), roomId);
            } else {
                sendError(ctx, "加入房间失败");
            }
        } catch (Exception e) {
            log.error("处理加入游戏消息失败", e);
            sendError(ctx, "加入游戏失败: " + e.getMessage());
        }
    }
    
    private void handleLeaveGame(ChannelHandlerContext ctx, String clientId, GameMessage message) {
        try {
            Player player = channelGroupManager.getPlayerByChannel(clientId);
            if (player != null) {
                String roomId = player.getRoomId();
                channelGroupManager.removePlayerFromRoom(player.getId(), ctx.channel());
                roomPlayers.remove(player.getId());
                playerRoomMap.remove(player.getId());
                
                broadcastRoomUpdate(roomId);
                log.info("玩家 {} 离开房间 {}", player.getUsername(), roomId);
            }
        } catch (Exception e) {
            log.error("处理离开游戏消息失败", e);
        }
    }
    
    private void handleUpdatePlayer(ChannelHandlerContext ctx, String clientId, GameMessage message) {
        try {
            Player player = objectMapper.convertValue(message.getData(), Player.class);
            log.info("收到玩家移动消息: 玩家ID={}, 房间ID={}, 位置=({}, {})", 
                player.getId(), player.getRoomId(), 
                player.getPosition().getX(), player.getPosition().getY());
            
            Player existingPlayer = roomPlayers.get(player.getId());
            
            if (existingPlayer != null) {
                existingPlayer.setPosition(player.getPosition());
                existingPlayer.setStatus(player.getStatus());
                
                String roomId = existingPlayer.getRoomId();
                log.info("广播玩家移动: 玩家={}, 房间={}, 位置=({}, {})", 
                    existingPlayer.getUsername(), roomId, 
                    existingPlayer.getPosition().getX(), existingPlayer.getPosition().getY());
                
                broadcastPlayerMove(roomId, existingPlayer, ctx.channel());
                log.info("玩家 {} 移动到位置 ({}, {})", player.getUsername(), 
                    player.getPosition().getX(), player.getPosition().getY());
            } else {
                log.warn("玩家 {} 不在房间中，无法更新位置", player.getId());
            }
        } catch (Exception e) {
            log.error("处理玩家移动消息失败", e);
        }
    }
    
    private void handleEmojiSent(ChannelHandlerContext ctx, String clientId, GameMessage message) {
        try {
            Player player = channelGroupManager.getPlayerByChannel(clientId);
            if (player != null && player.getRoomId() != null) {
                channelGroupManager.broadcastToRoomExcept(player.getRoomId(), 
                    new TextWebSocketFrame(objectMapper.writeValueAsString(message)), ctx.channel());
                log.info("玩家 {} 发送表情包", player.getUsername());
            }
        } catch (Exception e) {
            log.error("处理表情包消息失败", e);
        }
    }
    
    private String findOrCreateRoom(MBTISquare mbtiType) {
        String targetRoomId = null;
        int maxPlayers = 0;
        
        for (Map.Entry<String, MBTISquare> entry : roomMbtiMap.entrySet()) {
            if (mbtiType.equals(entry.getValue())) {
                String roomId = entry.getKey();
                int playerCount = channelGroupManager.getRoomPlayerCount(roomId);
                if (playerCount < 1000 && playerCount >= maxPlayers) {
                    maxPlayers = playerCount;
                    targetRoomId = roomId;
                }
            }
        }
        
        if (targetRoomId == null) {
            targetRoomId = mbtiType.name() + "_Room_" + System.currentTimeMillis();
            roomMbtiMap.put(targetRoomId, mbtiType);
        }
        
        return targetRoomId;
    }
    
    private Position generateRandomPosition(String roomId) {
        Random random = new Random();
        int x = random.nextInt(50);
        int y = random.nextInt(50);
        return new Position(x, y);
    }
    
    private void broadcastRoomUpdate(String roomId) {
        try {
            List<Player> players = getPlayersInRoom(roomId);
            GameMessage updateMessage = new GameMessage(MessageType.ROOM_UPDATE, "SERVER", roomId, players);
            String messageJson = objectMapper.writeValueAsString(updateMessage);
            channelGroupManager.broadcastToRoom(roomId, new TextWebSocketFrame(messageJson));
        } catch (Exception e) {
            log.error("广播房间更新失败", e);
        }
    }
    
    private void broadcastPlayerMove(String roomId, Player movedPlayer, Channel excludeChannel) {
        try {
            GameMessage moveMessage = new GameMessage(MessageType.PLAYER_MOVE, "SERVER", roomId, movedPlayer);
            String messageJson = objectMapper.writeValueAsString(moveMessage);
            
            log.info("准备广播玩家移动消息: 房间={}, 玩家={}, 消息={}", roomId, movedPlayer.getUsername(), messageJson);
            
            channelGroupManager.broadcastToRoomExcept(roomId, new TextWebSocketFrame(messageJson), excludeChannel);
            
            log.info("玩家移动消息广播完成: 房间={}, 玩家={}", roomId, movedPlayer.getUsername());
        } catch (Exception e) {
            log.error("广播玩家移动消息失败", e);
        }
    }
    

    
    private void sendError(ChannelHandlerContext ctx, String errorMessage) {
        try {
            GameMessage errorMsg = new GameMessage(MessageType.ERROR, "SERVER", null, errorMessage);
            String messageJson = objectMapper.writeValueAsString(errorMsg);
            ctx.channel().writeAndFlush(new TextWebSocketFrame(messageJson));
        } catch (Exception e) {
            log.error("发送错误消息失败", e);
        }
    }

    @Override
    public void handlerAdded(ChannelHandlerContext ctx) {
        String clientId = ctx.channel().id().asShortText();
        clients.put(clientId, ctx);
        log.info("客户端[{}]连接成功，当前在线人数: {}", clientId, clients.size());
    }

    @Override
    public void handlerRemoved(ChannelHandlerContext ctx) {
        String clientId = ctx.channel().id().asShortText();
        clients.remove(clientId);
        
        channelGroupManager.removeChannel(ctx.channel());
        
        log.info("客户端[{}]断开连接，当前在线人数: {}", clientId, clients.size());
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
        log.error("WebSocket异常", cause);
        ctx.close();
    }

    public static void broadcastToAll(String message) {
        clients.forEach((clientId, context) -> {
            context.channel().writeAndFlush(new TextWebSocketFrame(message));
        });
    }

    public static int getOnlineCount() {
        return clients.size();
    }
    
    public static List<Player> getPlayersInRoom(String roomId) {
        return roomPlayers.values().stream()
            .filter(player -> roomId.equals(player.getRoomId()))
            .toList();
    }
}
